﻿namespace OutLook.Module.Infrastructure
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using OutLook.Model.Domains;
    using OutLook.Model.Infrastructure;
    using OutLook.Module.Infrastructure.Interfaces;
    using System.IO;

    /// <summary>
    /// Xml Storage
    /// </summary>
    public abstract class XmlRepository : IRepository, ITestServiceModel
    {
        #region IRepository implementation

        public string GetStorageName<EntityType>() where EntityType : class
        {
            if (IsInitialized<EntityType>())
                return m_xmlEntities[typeof(EntityType)].Name;

            return null;
        }

        public bool IsInitialized<EntityType>() where EntityType : class
        {
            return m_xmlEntities.ContainsKey(typeof(EntityType)) && m_xmlEntities[typeof(EntityType)] != null;
        }

        public void Initialize<EntityType>(string connectionString) where EntityType :class
        {
            if (!IsInitialized<EntityType>())
            {
                m_xmlEntities[typeof(EntityType)] = new XMLStorage() { Name = connectionString };
                m_results[connectionString] = null;
                m_xmlEntities[typeof(EntityType)].GetData<EntityType>();
            }
        }

        public void Add<EntityType>(EntityType tableEntity) where EntityType : class
        {
            if (IsInitialized<EntityType>())
            {
                m_xmlEntities[typeof(EntityType)].Elements.Add(tableEntity);
                return;
            }

            throw new NotSupportedException(ModuleConstants.NOT_SUPPORTED_TYPE_EXCEPTION);
        }

        public void Update<EntityType>(EntityType tableEntity) where EntityType : class
        {
            if (IsInitialized<EntityType>())
            {
                int index = m_xmlEntities[typeof(EntityType)].Elements.IndexOf(tableEntity);
                if (index > -1)
                    m_xmlEntities[typeof(EntityType)].Elements[index] = tableEntity;
                else
                    throw new InvalidOperationException(ModuleConstants.NO_ENTITY);

                return;
            }

            throw new NotSupportedException(ModuleConstants.NOT_SUPPORTED_TYPE_EXCEPTION);
        }

        public void Delete<EntityType>(EntityType tableEntity) where EntityType : class
        {
            if (IsInitialized<EntityType>())
            {
                int index = m_xmlEntities[typeof(EntityType)].Elements.IndexOf(tableEntity);
                if (index > -1)
                    m_xmlEntities[typeof(EntityType)].Elements.RemoveAt(index);
                else
                    throw new InvalidOperationException(ModuleConstants.NO_ENTITY);

                return;
            }

            throw new NotSupportedException(ModuleConstants.NOT_SUPPORTED_TYPE_EXCEPTION);
        }

        public void Delete<EntityType>(object id) where EntityType : class
        {
            throw new NotImplementedException();
        }

        public abstract T Create<T>() where T : class, new();

        public T Item<T>(object key) where T : class
        {
            if (IsInitialized<T>())
            {
                List<T> collection = new List<T>(Enumerate<T>());

                if (key.GetType().Name.Equals(typeof(int).Name))
                    return collection[Convert.ToInt32(key)];

                return default(T);
            }

            throw new NotSupportedException(ModuleConstants.NOT_SUPPORTED_TYPE_EXCEPTION);
        }

        public IQueryable<EntityType> GetQuery<EntityType>() where EntityType : class
        {
            if (IsInitialized<EntityType>())
            {
                return Enumerate<EntityType>().AsQueryable<EntityType>();
            }

            throw new NotSupportedException(ModuleConstants.NOT_SUPPORTED_TYPE_EXCEPTION);
        }

        public IEnumerable<EntityType> Enumerate<EntityType>() where EntityType : class
        {
            if (IsInitialized<EntityType>())
            {
                List<EntityType> result = new List<EntityType>();
                foreach (EntityType item in m_xmlEntities[typeof(EntityType)].Elements)
                {
                    result.Add(item);
                }

                return result;
            }


            throw new NotSupportedException(ModuleConstants.NOT_SUPPORTED_TYPE_EXCEPTION);
        }

        public string GetStorageConnectionString<EntityType>() where EntityType : class
        {
            return string.Format("{0}.xml", Path.Combine(Resolver.ResolvePath(ModuleConstants.APPLICATION_DATA), CurrentCaseName, typeof(EntityType).Name));
        }

        #endregion

        #region IDisposable members

        ~XmlRepository()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposeManaged)
        {
            if (m_Disposed)
                return;

            if (disposeManaged)
            {
                foreach (IDisposable item in m_results.Values)
                {
                    if (item != null)
                        item.Dispose();
                }

                foreach (var elementCollection in m_xmlEntities.Values)
                {
                    foreach (IDisposable item in elementCollection.Elements)
                    {
                        if (item != null)
                            item.Dispose();
                    }
                    elementCollection.Elements.Clear();
                }
            }

            m_results.Clear();
            m_xmlEntities.Clear();

            ///Mark class as disposed
            m_Disposed = true;
        }

        #endregion

        #region ITestServiceModel

        private class XMLStorage
        {
            public string Name { get; set; }

            public List<object> Elements { get { return m_Entities; } }

            #region Methods

            /// <summary>
            /// Saves changes
            /// </summary>
            /// <typeparam name="T"></typeparam>
            public void SaveChanges<T>() where T : class
            {
                List<T> list = new List<T>();
                foreach (object item in m_Entities)
                {
                    list.Add(item as T);
                }

                DataSerializerHelper.XmlWrite<List<T>>(list, Name);
            }

            /// <summary>
            /// Gets data from storage
            /// </summary>
            /// <typeparam name="T"></typeparam>
            public void GetData<T>() where T : class
            {
                m_Entities.Clear();
                m_Entities.AddRange(DataSerializerHelper.XmlRead<List<T>>(Name));
            }

            #endregion

            private readonly List<object> m_Entities = new List<object>();
        }

        public string CurrentCaseName { get { return m_CurrentCaseName; } }

        public string CurrentModelTypeName { get { return m_CurrentModelTypeName; } }

        public abstract bool AnalysisResults();

        public IPathResolver Resolver { get; set; }

        /// <summary>
        /// Loads data for actions
        /// </summary>
        /// <param name=ModuleConstants.CASE_NAME_EXCEPTION>use case name</param>
        public void LoadTestData(string caseName)
        {
            LoadTestData(caseName, string.Empty);
        }

        /// <summary>
        /// Loads data for actions with one model
        /// </summary>
        /// <param name=ModuleConstants.CASE_NAME_EXCEPTION>action name (exmpl: validation)</param>
        /// <param name="modelType">model type name(exmpl: Product)</param>
        public void LoadTestData(string caseName, string modelType)
        {
            m_CurrentCaseName = caseName;
            m_CurrentModelTypeName = modelType;

            if (string.IsNullOrEmpty(caseName))
                throw new ArgumentException(ModuleConstants.CASE_NAME_EXCEPTION);

            LoadTestData();
        }

        protected abstract void LoadTestData();
     

        /// <summary>
        /// It needed for threading-safe singelton
        /// </summary>
        protected readonly object __Mutex = new object();

        private string m_CurrentCaseName = null;
        private string m_CurrentModelTypeName = null;
        private readonly Dictionary<Type, XMLStorage> m_xmlEntities = new Dictionary<Type, XMLStorage>();
        private readonly Dictionary<string, object> m_results = new Dictionary<string, object>();

        private bool m_Disposed = false;

        #endregion
    }

    /*public class XmlRepository : IRepository,ITestServiceModel
    {
        #region IRepository Implementation

        public string GetStorageName<T>() where T : class
        {
            if (IsInitialized<T>())
                return m_FilesNames[typeof(T)].FileName;

            return null;
        }

        public bool IsInitialized<T>() where T : class
        {
            return m_FilesNames.ContainsKey(typeof(T)) && m_FilesNames[typeof(T)] != null;
        }

        public void Initialize<T>(string connectionString) where T : class
        {
            if (!IsInitialized<T>())
            {
                m_FilesNames[typeof(T)] = new XmlStorage() { FileName = connectionString };
                m_FilesNames[typeof(T)].GetData<T>();
            }
        }

        public void Add<T>(T tableEntity) where T : class
        {
            if (IsInitialized<T>())
            {
                m_FilesNames[typeof(T)].GetData<T>();
                m_FilesNames[typeof(T)].Content.Add(tableEntity);
                m_FilesNames[typeof(T)].SaveChanges<T>();

                return;
            }

            throw new NotSupportedException("Entity type is not supported");
        }

        public void Update<T>(T tableEntity) where T : class
        {
            if (IsInitialized<T>())
            {
                m_FilesNames[typeof(T)].GetData<T>();
                int index = m_FilesNames[typeof(T)].Content.IndexOf(tableEntity);

                if (index > -1)
                    m_FilesNames[typeof(T)].Content[index] = tableEntity;
                else
                    throw new InvalidOperationException();

                m_FilesNames[typeof(T)].SaveChanges<T>();

                return;
            }

            throw new NotSupportedException("Entity type is not supported");
        }

        public void Delete<T>(T tableEntity) where T : class
        {
            if (IsInitialized<T>())
            {
                m_FilesNames[typeof(T)].GetData<T>();
                int index = m_FilesNames[typeof(T)].Content.IndexOf(tableEntity);

                if (index > -1)
                    m_FilesNames[typeof(T)].Content.RemoveAt(index);
                else
                    throw new InvalidOperationException();

                m_FilesNames[typeof(T)].SaveChanges<T>();

                return;
            }

            throw new NotSupportedException("Entity type is not supported");
        }

        public T Create<T>() where T : class
        {
            if (typeof(T) == typeof(ChemicalСombination))
                return new ChemicalСombination() as T;

            if (typeof(T) == typeof(Charge))
                return Charge.Create() as T;

            if (typeof(T) == typeof(ChargeRowToLoad))
                return ChargeRowToLoad.Create() as T;

            if (typeof(T) == typeof(Load))
                return new Load() as T;

            if (typeof(T) == typeof(Loss))
                return Loss.Create() as T;

            if (typeof(T) == typeof(LossRowToLoad))
                return LossRowToLoad.Create() as T;

            if (typeof(T) == typeof(Model))
                return Model.Create() as T;

            if (typeof(T) == typeof(SubstanceFactor))
                return new SubstanceFactor() as T;

            ///TODO:Create technology
            ///TODO:Create output

            throw new NotSupportedException("Entity type is not supported");
        }

        public T Item<T, Key>(Key key) where T : class
        {
            if (IsInitialized<T>())
            {
                m_FilesNames[typeof(T)].GetData<T>();

                if (typeof(Key) == typeof(int))
                    return m_FilesNames[typeof(T)].Content[Convert.ToInt32(key)] as T;

                return null;
            }

            throw new NotSupportedException("Entity type is not supported");
        }

        public IQueryable<T> GetQuery<T>() where T : class
        {
            return Enumerate<T>().AsQueryable<T>();
        }

        public IEnumerable<T> Enumerate<T>() where T : class
        {
            if (IsInitialized<T>())
            {
                List<T> result = new List<T>();
                foreach (var item in m_FilesNames[typeof(T)].Content)
                {
                    result.Add(item as T);
                }
                return result;
            }

            throw new NotSupportedException("Entity type is not supported");
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool managedDispose)
        {
            if (m_Dispose)
                return;

            if (managedDispose)
            {
                ///TODO:maneger resources
            }

            m_FilesNames.Clear();

            m_Dispose = true;
        }

        #endregion

        #region My Xml Mock

        private class XmlStorage
        {
            #region Properties

            public string FileName
            {
                get { return m_FileName; }
                set { m_FileName = value; }
            }

            public List<object> Content
            {
                get { return m_Content; }
            }

            #endregion

            #region Methods

            /// <summary>
            /// Saves changes
            /// </summary>
            /// <typeparam name="T"></typeparam>
            public void SaveChanges<T>() where T : class
            {
                List<T> list = new List<T>();
                foreach (object item in m_Content)
                {
                    list.Add(item as T);
                }

                DataSerializerHelper.XmlWrite<List<T>>(list, m_FileName);
            }

            /// <summary>
            /// Gets data from storage
            /// </summary>
            /// <typeparam name="T"></typeparam>
            public void GetData<T>() where T : class
            {
                m_Content.Clear();
                m_Content.AddRange(DataSerializerHelper.XmlRead<List<T>>(m_FileName));
            }

            #endregion

            #region Fields

            private string m_FileName = default(string);
            private readonly List<object> m_Content = new List<object>();

            #endregion
        }

        #endregion

        #region Private Fields

        private readonly Dictionary<Type, XmlStorage> m_FilesNames = new Dictionary<Type, XmlStorage>();
        private bool m_Dispose = false;

        #endregion
    }*/
}
